package client

import (
	"errors"
	"fmt"
	"gitee.com/slh1992/plugin-sip/sip"
	"gitee.com/slh1992/plugin-sip/transaction"
	"gitee.com/slh1992/plugin-sip/tu"
	"net"
	"net/http"
	"strings"
	"time"
)

const FFMPEG_TO_FLV = "FFmpeg to FLV"
const SIP_STATUS_ON = "ON"
const SIP_STATUS_OFF = "OFF"
const REGIST_STATUS_OK = "1"
const REGIST_STATUS_FATIL = "0"

type SipClient struct {
	*transaction.Core `json:"-"`
	ID                string
	sn                int
	LocalIp           string
	from              *sip.Contact
	to                *sip.Contact
	RegisterTime      time.Time
	UpdateTime        time.Time
	LastKeepaliveAt   time.Time
	TryCnt            uint
	Done              chan struct{}
	MinPort           uint16
	MaxPort           uint16
	Status            string
	handle            SipHandleFunc
}

type SipHandleFunc interface {
	HandleRegister(req *sip.Request, tx *transaction.GBTx)
	HandleInvite(req *sip.Request, tx *transaction.GBTx)
	HandleMessage(req *sip.Request, tx *transaction.GBTx)
	HandleAck(req *sip.Request, tx *transaction.GBTx)
	HandleByte(req *sip.Request, tx *transaction.GBTx)
	HandleSubsribe(req *sip.Request, tx *transaction.GBTx)
	KeepLiveCall(deviceId, status string)
	RegisterCall(deviceId, status string)
}

func NewClient(config *transaction.Config) (*SipClient, error) {
	if config.SipIP == "" {
		return nil, errors.New("Sip服务器地址不能为空！")
	}
	if config.SipPort == 0 {
		return nil, errors.New("Sip服务器端口号未设置！")
	}
	if config.LocPort == 0 {
		config.LocPort = 15060
	}
	if config.Username == "" {
		return nil, errors.New("Sip服务器用户名不能为空！")
	}
	if config.Serial == "" {
		return nil, errors.New("Sip服务器序列不能为空！")
	}
	if config.Realm == "" {
		return nil, errors.New("Sip服务器域不能为空！")
	}
	client := &SipClient{
		sn:         1,
		UpdateTime: time.Now(),
		TryCnt:     0,
		Done:       make(chan struct{}),
		Status:     REGIST_STATUS_FATIL,
	}
	c, err := transaction.NewCliCore(config)
	if err != nil {
		return nil, err
	}
	client.Core = c
	client.ID = config.Username
	return client, nil
}

func (c *SipClient) SetUp(handle SipHandleFunc) error {
	c.RegistHandler(sip.REGISTER, handle.HandleRegister)
	c.RegistHandler(sip.INVITE, handle.HandleInvite)
	c.RegistHandler(sip.MESSAGE, handle.HandleMessage)
	c.RegistHandler(sip.ACK, handle.HandleAck)
	c.RegistHandler(sip.BYE, handle.HandleByte)
	c.RegistHandler(sip.SUBSCRIBE, handle.HandleSubsribe)
	c.handle = handle
	err := c.StartAndWait()
	if err != nil {
		return err
	}
	time.Sleep(1 * time.Second)
	if c.LocalIp == "" {
		c.LocalIp = c.GetLocalIp()
	}
	go func(c *SipClient) {
		defer func() {
			fmt.Sprintf("%s>>>>>>心跳已停止:%s\n", time.Now().String(), c.SipIP)
		}()
		timer := time.NewTicker(time.Duration(c.HeartbeatInterval) * time.Second)
		for {
			select {
			case <-timer.C:
				//校验过期就重新注册
				go c.checkRegisterExpires()
				c.sendHeartMsg()
			case <-c.Done:
				return
			}
		}
	}(c)

	return c.toRegister()
}

func (c *SipClient) sendHeartMsg() {
	keeplive := sip.BuildKeepLvieXML(c.sn, c.Username)
	msg := c.CreateMessage(sip.MESSAGE, keeplive)
	resp, err := c.SipRequestForResponse(&sip.Request{Message: msg})
	if resp == nil || err != nil {
		fmt.Printf("%s心跳发送失败[%d]:%s\n", c.SipIP, c.TryCnt, err.Error())
		opErr, ok := err.(*net.OpError)
		if ok && strings.Index(opErr.Error(), "closed network connection") != -1 {
			c.resetWait()
			return
		}
		c.TryCnt++
		if c.TryCnt > uint(c.HeartbeatRetry) {
			fmt.Printf("%s心跳发送超出限制[%d]次，停止心跳机制\n", c.SipIP, c.HeartbeatRetry)
			c.handle.KeepLiveCall(c.Username, SIP_STATUS_OFF)
			c.UnRegister()
			return
		}
		if c.TryCnt == uint(c.HeartbeatRetry) {
			err = c.toRegister()
			if err != nil {
				fmt.Printf(">>>>>>>>>>>>>>%s\n", err.Error())
			}
		}
		return
	}
	if resp.StartLine.Code == http.StatusOK {
		//当心跳成功且前一次失败，说明sip服务器存在失联情况，必须重新注册
		if c.TryCnt > 0 && c.TryCnt < uint(c.HeartbeatRetry) {
			err = c.toRegister()
			if err != nil {
				fmt.Printf(">>>>>>>>>>>>>>>%s\n", err.Error())
			}
		}
		fmt.Printf("======%s心跳发送成功,失败[%d]次=====\n", c.SipIP, c.TryCnt)
		c.TryCnt = 0
		c.LastKeepaliveAt = time.Now()
		c.UpdateTime = time.Now()
		c.handle.KeepLiveCall(c.Username, SIP_STATUS_ON)
		return
	}
}

func (c *SipClient) checkRegisterExpires() {
	expireTimes := c.RegisterTime.Add(time.Duration(c.RegisterValidity) * time.Second)
	if time.Now().After(expireTimes) {
		err := c.toRegister()
		if err != nil {
			c.handle.RegisterCall(c.Username, REGIST_STATUS_FATIL)
			return
		}
		c.handle.RegisterCall(c.Username, REGIST_STATUS_OK)
	}
}

func (c *SipClient) toRegister() error {
	msg := c.CreateMessage(sip.REGISTER, "")
	resp, err := c.SipRequestForResponse(&sip.Request{Message: msg})
	if resp == nil {
		c.destory()
		return errors.New("服务注册失败：" + err.Error())
	}
	if resp.StartLine.Code == http.StatusUnauthorized {
		msg := c.CreateMessage(sip.REGISTER, "")
		msg.Authorization = sip.ToAuthorization(resp.To.Uri.UserInfo(), msg.From.Uri.String(), c.Password, resp.WwwAuthenticate)
		resp, err = c.SipRequestForResponse(&sip.Request{Message: msg})
		if resp == nil {
			c.destory()
			return errors.New("服务注册失败：" + err.Error())
		}
		if resp.GetStatusCode() != http.StatusOK {
			c.destory()
			return errors.New("服务注册不成功")
		}
	}
	if resp.GetStatusCode() == http.StatusOK {
		c.RegisterTime = time.Now()
		c.UpdateTime = time.Now()
		c.Status = REGIST_STATUS_OK
		c.TryCnt = 0
		c.sendHeartMsg()
	}
	return nil
}

func (c *SipClient) UnRegister() {
	msg := c.CreateMessage(sip.REGISTER, "")
	msg.Expires = 0
	resp, err := c.SipRequestForResponse(&sip.Request{msg})
	if err != nil || resp == nil {
		c.destory()
		return
	}
	if resp.StartLine.Code == http.StatusUnauthorized {
		msg := c.CreateMessage(sip.REGISTER, "")
		msg.Authorization = sip.ToAuthorization(resp.To.Uri.UserInfo(), msg.From.Uri.String(), c.Password, resp.WwwAuthenticate)
		msg.Expires = 0
		resp, err = c.SipRequestForResponse(&sip.Request{Message: msg})
		if resp == nil {
			c.destory()
			return
		}
		if resp.GetStatusCode() != http.StatusOK {
			c.destory()
			return
		}
	}
	if resp.GetStatusCode() == http.StatusOK {
		c.destory()
	}

}
func (c *SipClient) destory() {
	c.Status = REGIST_STATUS_FATIL
	close(c.Done)
	c.Close()
}

func (c *SipClient) resetWait() error {
	c.Close()
	err := c.StartAndWait()
	if err != nil {
		fmt.Printf(">>>>>>>>>>>>>重新发起连接：%s\n", c.SipIP)
		return err
	}
	c.toRegister()
	return nil
}

func (c *SipClient) CreateMessage(mehod sip.Method, body string) *sip.Message {
	c.sn++
	return tu.BuildClientMessageRequest(mehod, c.SipNetwork, c.Serial, c.Realm, c.Username, c.LocalIp, c.LocPort, c.RegisterValidity, c.sn, body)
}
